package comp.comp.wxxcx_login.common.config;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import com.justep.cloud.repo.BaseRepositoryImpl;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.logging.Log4jFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.util.JdbcConstants;
import com.alibaba.druid.util.JdbcUtils;
import com.google.common.collect.Lists;
import com.justep.cloud.x5.DialectHelper;
import com.justep.comp.util.XMLUtil;
import com.justep.service.config.api.ServiceConfig;
import com.justep.service.config.api.ServiceConfigFactory;

@Configuration
@EnableJpaRepositories(repositoryBaseClass=BaseRepositoryImpl.class,basePackages = { "/comp.comp.wxxcx_login.**.repository" },
	entityManagerFactoryRef = "moduleEntityManagerFactory",transactionManagerRef="moduleTransactionManager")
public class JarsConfig {
	private static final Logger LOGGER = LoggerFactory.getLogger(JarsConfig.class);
	
	private String serviceName = "comp.comp.wxxcx_login";
	
	private String moduleName = "COMP_WXXCX_LOGIN";
	
	private DBResource resource;
	
	public JarsConfig() throws Exception {
		resource = new DBResource();
	}

	@Bean
	public SpringContextUtils springContextUtils() {
		return new SpringContextUtils();
	}
	
	@Bean(name = "localeResolver")
	public CookieLocaleResolver cookieLocaleResolver() {
		CookieLocaleResolver cookie = new CookieLocaleResolver();
		return cookie;
	}
	
	@Bean
	public MyDispatcher myDispatcher() {
		springContextUtils();
		return new MyDispatcher(SpringContextUtils.getApplicationContext());
	}
	
	@Bean(value = "druidLogFilter")
	public Log4jFilter druidLogFilter(){
		Log4jFilter filter = new Log4jFilter();
		filter.setStatementExecutableSqlLogEnable(true);
		return filter;
	}
	
	
	@Bean(value = "moduleDataSource", initMethod = "init", destroyMethod = "close")
	public DruidDataSource moduleDataSource() {
		DruidDataSource datasource = new DruidDataSource();

		datasource.setUrl(resource.getUrl());
		datasource.setUsername(resource.getUsername());
		datasource.setPassword(resource.getPassword());
		datasource.setDriverClassName(resource.getDriverClassName());

		// configuration
		datasource.setInitialSize(1);
		datasource.setMinIdle(1);
		datasource.setMaxActive(20);
		datasource.setMaxWait(5000);
		datasource.setTimeBetweenEvictionRunsMillis(5000);
		datasource.setMinEvictableIdleTimeMillis(30000);
		datasource.setTestWhileIdle(true);
		datasource.setTestOnBorrow(false);
		datasource.setTestOnReturn(false);
		datasource.setPoolPreparedStatements(true);
		datasource.setValidationQuery("select 1 ");
		datasource.setMaxPoolPreparedStatementPerConnectionSize(20);
		datasource.setConnectionErrorRetryAttempts(0);
		List<Filter> filters= Lists.newArrayList(druidLogFilter());
		datasource.setProxyFilters(filters);

		return datasource;
	}

	@Bean("moduleEntityManagerFactory")
	public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
		LOGGER.info("生成entityManagerFactory");
		LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
		
		factory.setDataSource(moduleDataSource());
		factory.setPackagesToScan("comp.comp.wxxcx_login");
		factory.setJpaVendorAdapter(jpaVendorAdapter());
		//为避免hibernate 自动生成外键，导致migrate数据迁移时失败，应禁用hibernate的数据库更新功能
		//mysql上 migrate对于Float类型的字段会创建为double,这里设置为none，如果设置为validate，hibernate会在启动时报错
		factory.getJpaPropertyMap().put("hibernate.dialect", resource.getDialect());
		factory.getJpaPropertyMap().put("hibernate.show_sql", "false");
		factory.getJpaPropertyMap().put("hibernate.temp.use_jdbc_metadata_defaults", "false");
		//factory.getJpaPropertyMap().put("hibernate.hbm2ddl.auto", "none");
		return factory;
	}
	
	@Bean("hibernateJpaVendorAdapter")
	public JpaVendorAdapter jpaVendorAdapter() {
		HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
		return adapter;
	}
	
	class DBResource {
		private String url;
		private String dialect;
		private String username;
		private String password;
		private String driverClassName;
		private String dbHost;
		private String dbType;
		private String dbName;
		private String properties;
		
		private String dbPort;
		private String dbSchema;
		
		private Element root;
		private boolean fromEnv =false;
		
		public DBResource() throws Exception {
			ServiceConfig config = ServiceConfigFactory.get(serviceName);
			dbHost = config.getVar(moduleName+"_DBHOST");
			if (StringUtils.isBlank(dbHost)) {
				try {
					File datasourceLocation = new File(System.getenv("JUSTEP_HOME"),"conf/datasource.xml");
					LOGGER.info("获取数据源 {}",datasourceLocation.getPath());
					root = XMLUtil.readXML_SAX(datasourceLocation);
				} catch (FileNotFoundException | DocumentException e) {
					LOGGER.error("数据源配置文件读取错误！", e);
				}
			}else {
				dbType = config.getVar(moduleName+"_DBTYPE");
				dbName = config.getVar(moduleName+"_DBNAME");
				dbPort = config.getVar(moduleName+"_DBPORT");
				dbSchema = config.getVar(moduleName+"_DBSCHEMA");
				username = config.getVar(moduleName+"_DBUSER");
				password = config.getVar(moduleName+"_DBPASSWD");
				properties = config.getVar(moduleName+"_DBPROPERTIES");
				
				fromEnv = true;
			}
			init();
			LOGGER.info("db resource url from env {} : {}",fromEnv,url);
		}
		
		private void init() throws Exception {
			if (fromEnv) {
				url = genJDBCUrl();
				driverClassName = JdbcUtils.getDriverClassName(url);
				dialect = DialectHelper.getDialect(url,driverClassName);
				if (StringUtils.equals(JdbcUtils.getDbType(url, driverClassName), "jtds")) {
					dialect = "org.hibernate.dialect.SQLServerDialect";
				}
			}else {
				Element resource = root.element("Resource");
				String attrUrl = resource.attributeValue("url");
				url = attrUrl.substring(0,attrUrl.indexOf("?"));
				username = resource.attributeValue("username");
				password = resource.attributeValue("password");
				driverClassName = resource.attributeValue("driverClassName");
				dialect = DialectHelper.getDialect(url, driverClassName);
			}
		}
		
		private String genJDBCUrl() {
			if (dbType.contains("oracle")) {
				return "jdbc:oracle:thin"+":@"+dbHost+":"+dbPort+":"+dbName;
			}
			else if (dbType.contains("pgsql")) {
				return "jdbc:postgresql"+"://"+dbHost+":"+dbPort+"/"+dbName;
			}
			else if (dbType.contains("mysql")) {
				if (StringUtils.isEmpty(properties)) {
					return "jdbc:mysql"+"://"+dbHost+":"+dbPort+"/"+dbName+"?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8";
				} else {
					String props = properties.replace(",", "&");
					return "jdbc:mysql"+"://"+dbHost+":"+dbPort+"/"+dbName+"?"+props;
				}
			}
			else if (dbType.contains("sqlserver")) {
				if (StringUtils.isEmpty(properties)) {
//					return "jdbc:"+"sqlserver"+"://"+dbHost+":"+dbPort+";databaseName="+dbName+";user="+username+";password="+password;
					return "jdbc:jtds:sqlserver"+"://"+dbHost+":"+dbPort+";databaseName="+dbName+";";
				} else {
					String props = properties.replace(",", ";");
					return "jdbc:jtds:sqlserver"+"://"+dbHost+":"+dbPort+";databaseName="+dbName+";"+props;
				}
			}
			throw new IllegalArgumentException("generate JDBC url error!");
		}

		public String getUrl() {
			return url;
		}
		public void setUrl(String url) {
			this.url = url;
		}
		public String getDialect() {
			return dialect;
		}
		public void setDialect(String dialect) {
			this.dialect = dialect;
		}
		public String getUsername() {
			return username;
		}
		public void setUsername(String username) {
			this.username = username;
		}
		public String getPassword() {
			return password;
		}
		public void setPassword(String password) {
			this.password = password;
		}
		public String getDriverClassName() {
			return driverClassName;
		}
		public void setDriverClassName(String driverClassName) {
			this.driverClassName = driverClassName;
		}
	}
}